#include "EpilBooleanObject.h"
#include "EpilObjectHelper.h"
#include "EpilInterpreter.h"
#include "EpilInterpreterException.h"


EpilBooleanObject::EpilBooleanObject( EpilInterpreter * interpreter, bool value )
: EpilValueObject( interpreter )
, m_value( value )
{
}


EpilBooleanObject::~EpilBooleanObject()
{
}


EpilObject * EpilBooleanObject::OperateUnary( EpilUnaryOperator op )
{
	switch( op )
	{
	case EUO_Opposite:
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a boolean value.") );
	case EUO_Length:
		throw EpilInterpreterException( t("Attempt to get length of a boolean value.") );
	case EUO_Not:
		return this->GetInterpreter()->CreateBooleanObject( ! this->GetValue() );
	}
	throw EpilInterpreterException( t("Invalid unary operator detected.") );
}


EpilObject * EpilBooleanObject::OperateBinary( EpilBinaryOperator op, EpilObject * that )
{
	switch( that->GetType() )
	{
	case EOT_Nil:
		{
			EpilNilObject * thatNil = static_cast<EpilNilObject*>( that );
			return this->OperateBinaryInternal( op, thatNil );
		}
	case EOT_Boolean:
		{
			EpilBooleanObject * thatBoolean = static_cast<EpilBooleanObject*>( that );
			return this->OperateBinaryInternal( op, thatBoolean );
		}
	case EOT_Number:
		{
			EpilNumberObject * thatDouble = static_cast<EpilNumberObject*>( that );
			return this->OperateBinaryInternal( op, thatDouble );
		}
	case EOT_String:
		{
			EpilStringObject * thatStr = static_cast<EpilStringObject*>( that );
			return this->OperateBinaryInternal( op, thatStr );
		}
	}
	return NULL;
}


EpilObject * EpilBooleanObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNilObject * that )
{
	switch( op )
	{
	case EBO_And:
		if( this->GetValue() == true )
		{
			return this->GetInterpreter()->CreateNilObject();
		}
		else
		{
			return this->GetInterpreter()->CreateBooleanObject( false );
		}
	case EBO_Or:
		if( this->GetValue() == true )
		{
			return this->GetInterpreter()->CreateBooleanObject( true );
		}
		else
		{
			return this->GetInterpreter()->CreateNilObject();
		}
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare boolean with nil.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a boolean value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a boolean value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilBooleanObject::OperateBinaryInternal( EpilBinaryOperator op, EpilBooleanObject * that )
{
	switch( op )
	{
	case EBO_And:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() && that->GetValue() );
	case EBO_Or:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() || that->GetValue() );
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() == that->GetValue() );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( this->GetValue() != that->GetValue() );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare two boolean values.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a boolean value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a boolean value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilBooleanObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNumberObject * that )
{
	switch( op )
	{
	case EBO_And:
		if( this->GetValue() == true )
		{
			return that->CreateCopy();
		}
		else
		{
			return this->GetInterpreter()->CreateBooleanObject( false );
		}
	case EBO_Or:
		if( this->GetValue() == true )
		{
			return this->GetInterpreter()->CreateBooleanObject( true );
		}
		else
		{
			return that->CreateCopy();
		}
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare boolean with number.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a boolean value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a boolean value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilBooleanObject::OperateBinaryInternal( EpilBinaryOperator op, EpilStringObject * that )
{
	switch( op )
	{
	case EBO_And:
		if( this->GetValue() == true )
		{
			return that->CreateCopy();
		}
		else
		{
			return this->GetInterpreter()->CreateBooleanObject( false );
		}
	case EBO_Or:
		if( this->GetValue() == true )
		{
			return this->GetInterpreter()->CreateBooleanObject( true );
		}
		else
		{
			return that->CreateCopy();
		}
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare boolean with string.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a boolean value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a boolean value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


bool EpilBooleanObject::operator==( bool rhs ) const
{
	return m_value == rhs;
}


bool EpilBooleanObject::operator==( const EpilObject & rhs ) const
{
	if( rhs.GetType() != EOT_Boolean )
	{
		return false;
	}
	const EpilBooleanObject & obj = static_cast<const EpilBooleanObject &>( rhs );
	return *this == obj;
}


bool EpilBooleanObject::operator==( const EpilBooleanObject & rhs ) const
{
	return m_value == rhs.GetValue();
}


EpilValueObject * EpilBooleanObject::Clone() const
{
	EpilBooleanObject * ret = this->GetInterpreter()->CreateBooleanObject( m_value );
	return ret;
}


tstring EpilBooleanObject::ToString() const
{
	return EpilObjectHelper::ToString( m_value );
}